﻿# -*- coding: utf_8 -*-
from Bone import Bone
import xml.dom.minidom as minixml
import copy
import wx
import os

class DlgMeshSelection(wx.Dialog):
	def __init__(self, parent, IGameXMLDoc):
		wx.Dialog.__init__(self, parent, title = 'Select meshes that need to be exported', size = (400, 600))
		
		self._meshExportList = []
		self._treeMeshNode = wx.TreeCtrl(self, -1, style = wx.TR_HIDE_ROOT|wx.TR_MULTIPLE)
		self._bnOk = wx.Button(self, wx.ID_OK, 'OK')
		self._bnCancel = wx.Button(self, wx.ID_CANCEL, 'Cancel')
		
		sizerButton = wx.BoxSizer(wx.HORIZONTAL)
		sizerButton.Add(self._bnOk, 0, border = 5)
		sizerButton.Add(self._bnCancel, 0, border = 5)
		
		sizer = wx.BoxSizer(wx.VERTICAL)
		sizer.Add(self._treeMeshNode, 1, wx.ALL|wx.EXPAND, border = 5)
		sizer.Add(sizerButton, 0, wx.ALL, border = 5)
		
		self.SetSizer(sizer)
		
		self.Bind(wx.EVT_BUTTON, self.OnClickBnOk, self._bnOk)
		
		self._root = self._treeMeshNode.AddRoot('IGame XML')
		for node in IGameXMLDoc.childNodes:
			if node.nodeName == 'IGame': 
				self.AddMeshNode(node, self._root)
		self._treeMeshNode.ExpandAll()
		
	def AddMeshNode(self, node, treeItem):
		for n in node.childNodes:
			if n.nodeName == 'Node' and n.getAttribute('NodeType') == 'Mesh':
				i = self._treeMeshNode.AppendItem(treeItem, n.getAttribute('Name'))
				self.AddMeshNode(n, i)
			
	def OnClickBnOk(self, e):
		self._meshExportList = []
		for sel in self._treeMeshNode.GetSelections():
			self._meshExportList.append(self._treeMeshNode.GetItemText(sel))
				
		self.EndModal(wx.ID_OK)
	
	def GetMeshExportList(self):
		return self._meshExportList        

class D3DVertex:
	def __init__(self):
		self._pos = [0, 0, 0]
		self._normal = [0, 0, 0]
		self._uv = [0, 0]
		self._original = -1

class VertexSkin:
	def __init__(self):
		self._bone = [] ## (boneId, weight, boneIdx)
				
class SubMesh:
	def __init__(self):
		self._d3dVertexList = []
		self._d3dFaceIdxList = []
		
		self._materialIndex = -1
		self._subMaterialIndex = -1

		self._outputFile = ''
		self._outputPath = ''
		self._meshName = ''
			
class CMeshExport:
	def __init__(self):
		self._vertexSkin = {}
		self._subMeshList = []
			
	def HasSkin(self):
		return len(self._vertexSkin) != 0
	
	def DoExport(self, xmlDoc, nodeName, pathName):
		print 'Export mesh node: %s' % nodeName
		for node in xmlDoc.childNodes:
			if node.nodeName == 'IGame':
				igame = node
				for node in igame.childNodes:
					if node.nodeName == 'Node' and node.getAttribute('NodeType') == 'Mesh' and node.getAttribute('Name') == nodeName:
						self.__ParseMeshNode(node, int(node.getAttribute('MaterialIndex')))
						break

		self._meshName = nodeName
		self._outputPath = pathName

	def ProcessSkinBoneId(self, skeletonExporter):
		""" find each bone's index from bone ID """
		
		for subMesh in self._subMeshList:
			self.__ProcessSkinBoneId(subMesh, skeletonExporter)
					
	def __ProcessSkinBoneId(self, subMesh, skeletonExporter):        
			for vs in self._vertexSkin.values():
					c = 0
					for bone in vs._bone:
							for i in xrange(0, len(skeletonExporter._boneList)):
									b = skeletonExporter._boneList[i]
									if b._id == bone[0]:
											bone[2] = i
											c += 1
											break

					if c != len(vs._bone):
							print self._vertexSkin.values().index(vs), vs._bone
					assert(c == len(vs._bone))

	def __ParseMeshNode(self, meshNode, materialIndex):
		
			for node in meshNode.childNodes:
					if node.nodeName == 'Modifiers':
							for subNode in  node.childNodes:
									if subNode.nodeName ==  'Modifier':
											modName = subNode.getAttribute('modName')
											isSkin = subNode.getAttribute('IsSkin')
											skinType = subNode.getAttribute('SkinType')
											if (modName == 'SkinMod' or modName == 'Skin') and\
												 isSkin == 'true' and\
												 (skinType == 'Physique' or skinType == 'MaxSkin'):
													self.__ParseSkinInfo(subNode)
					elif node.nodeName == 'D3DSubMeshList':
							for subMeshNode in node.childNodes:
									if subMeshNode.nodeName == 'SubMesh':
											self.__ParseSubMeshNode(subMeshNode, materialIndex)
											
	def __ParseSubMeshNode(self, subMeshNode, materialIndex):
			subMesh = SubMesh()
			subMesh._materialIndex = materialIndex
			subMesh._subMaterialIndex = int(subMeshNode.getAttribute('SubMaterialID'))
			self._subMeshList.append(subMesh)
			for node in subMeshNode.childNodes:
					if node.nodeName == 'D3DVertex':
							self.__ParseD3DVertex(subMesh, node)
					elif node.nodeName == 'D3DFace':
							self.__ParseD3DFace(subMesh, node)
							print 'D3D vertex count: [%d] D3D face count: [%d]' % (len(subMesh._d3dVertexList), len(subMesh._d3dFaceIdxList))

	def __ParseD3DVertex(self, subMesh, d3dNode):
			for vertexNode in d3dNode.childNodes:
					if vertexNode.nodeName == 'Vertex':
							v = D3DVertex()
							v._pos = map(float, vertexNode.getAttribute('Pos').split(' '))
							v._normal = map(float, vertexNode.getAttribute('Normal').split(' '))
							v._uv = map(float, vertexNode.getAttribute('UV').split(' '))
							v._original = int(vertexNode.getAttribute('Original'))
							subMesh._d3dVertexList.append(v)

	def __ParseD3DFace(self, subMesh, d3dNode):
			for faceNode in d3dNode.childNodes:
					if faceNode.nodeName == 'Face':
							face = map(int, faceNode.getAttribute('face').split(' '))
							subMesh._d3dFaceIdxList.append(face)

	def __ParseSkinInfo(self, skinNode):
		for n in skinNode.childNodes:
			if n.nodeName == 'Skin':
				vs = VertexSkin()
				vertexId = int(n.getAttribute('VertexID'))
				t = n.getAttribute('Type')
				if t == 'Rigid':
					for bone in n.childNodes:
						if bone.nodeName == 'Bone':
							boneId  = bone.getAttribute('BoneID')
							vs._bone.append([boneId, 1.0, -1])
							break
				elif t == 'Blended':
					w = 0
					for bone in n.childNodes:
						if bone.nodeName == 'Bone':
							boneId  = bone.getAttribute('BoneID')
							weight = float(bone.getAttribute('Weight'))
							w += weight
							if weight != 0.0:
								vs._bone.append([boneId, weight, -1])

					if len(vs._bone) > 4:
						print 'ERROR: blend bone count: %d greater than 4' % len(vs._bone)

					if w != 1.0:
						print 'Weight sum is not 1.0. VertexID: %d Weight: %f' % (vertexId, w)

				self._vertexSkin[vertexId] = vs

	def WriteFile(self):
			for subMesh in self._subMeshList:
					self.__WriteMeshFile(subMesh)
			
	def __WriteMeshFile(self, subMesh):
			if not subMesh._d3dVertexList or not subMesh._d3dFaceIdxList:
					return
			
			try:
					impl = minixml.getDOMImplementation()
					newdoc = impl.createDocument(None, None, None)

					meshElem = newdoc.createElement('Mesh')
					meshElem.setAttributeNode(newdoc.createAttribute('VertexNum'))
					meshElem.setAttribute('VertexNum', str(len(subMesh._d3dVertexList)))
					meshElem.setAttributeNode(newdoc.createAttribute('FaceNum'))
					meshElem.setAttribute('FaceNum', str(len(subMesh._d3dFaceIdxList)))
					meshElem.setAttributeNode(newdoc.createAttribute('Type'))
					if self.HasSkin():
							meshElem.setAttribute('Type', 'SkinMesh')
					else:
							meshElem.setAttribute('Type', 'Static')
					newdoc.childNodes.append(meshElem)

					verticesElem = newdoc.createElement('Vertices')
					meshElem.childNodes.append(verticesElem)

					for i in xrange(0, len(subMesh._d3dVertexList)):
							vert = subMesh._d3dVertexList[i]

							vertElem = newdoc.createElement('Vertex')
							verticesElem.childNodes.append(vertElem)

							posElem = newdoc.createElement('pos')
							vertElem.childNodes.append(posElem)

							posElem.setAttributeNode(newdoc.createAttribute('x'))
							posElem.setAttribute('x', str(vert._pos[0]))

							posElem.setAttributeNode(newdoc.createAttribute('y'))
							posElem.setAttribute('y', str(vert._pos[2]))

							posElem.setAttributeNode(newdoc.createAttribute('z'))
							posElem.setAttribute('z', str(vert._pos[1]))

							normalElem = newdoc.createElement('normal')
							vertElem.childNodes.append(normalElem)

							normalElem.setAttributeNode(newdoc.createAttribute('x'))
							normalElem.setAttribute('x', str(vert._normal[0]))

							normalElem.setAttributeNode(newdoc.createAttribute('y'))
							normalElem.setAttribute('y', str(vert._normal[2]))

							normalElem.setAttributeNode(newdoc.createAttribute('z'))
							normalElem.setAttribute('z', str(vert._normal[1]))

							uvElem = newdoc.createElement('uv')
							vertElem.childNodes.append(uvElem)

							uvElem.setAttributeNode(newdoc.createAttribute('u'))
							uvElem.setAttribute('u', str(vert._uv[0]))

							uvElem.setAttributeNode(newdoc.createAttribute('v'))
							uvElem.setAttribute('v', str(1.0 - vert._uv[1]))

							if self._vertexSkin:
									skinElem = newdoc.createElement('skin')
									vertElem.childNodes.append(skinElem)

									for bone in self._vertexSkin[vert._original]._bone:
											boneElem = newdoc.createElement('bone')
											skinElem.childNodes.append(boneElem)

											boneElem.setAttributeNode(newdoc.createAttribute('boneIdx'))
											boneElem.setAttribute('boneIdx', str(bone[2]))

											boneElem.setAttributeNode(newdoc.createAttribute('weight'))
											boneElem.setAttribute('weight', str(bone[1]))

					faceElem = newdoc.createElement('Face')
					meshElem.childNodes.append(faceElem)
					for face in subMesh._d3dFaceIdxList:
							elem = newdoc.createElement('face')
							faceElem.childNodes.append(elem)

							elem.setAttributeNode(newdoc.createAttribute('x'))
							elem.setAttribute('x', str(face[0]))

							elem.setAttributeNode(newdoc.createAttribute('y'))
							elem.setAttribute('y', str(face[1]))

							elem.setAttributeNode(newdoc.createAttribute('z'))
							elem.setAttribute('z', str(face[2]))

					try: os.makedirs(self._outputPath)
					except: pass
					subMesh._outputFile = os.path.join(self._outputPath, '%s_m%d-%d.mesh.xml' % (self._meshName, subMesh._materialIndex, subMesh._subMaterialIndex))
					print 'Write mesh file: %s' % subMesh._outputFile
					newdoc.writexml(open(subMesh._outputFile, 'w'),    newl = '\r\n', addindent = '    ', encoding = 'utf-8')
					newdoc.unlink()

			except:
					SHOW_TRACE()
					
					
					